/**********************************************************************
// @@@ START COPYRIGHT @@@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
//
// @@@ END COPYRIGHT @@@
**********************************************************************/
/* -*-C++-*-
******************************************************************************
*
* File:         RelPackedRows.cpp
* Description:  All the methods of UnPackRows() and PhysUnPackRows().
*
* Created:      6/27/97
* Language:     C++
*
*
******************************************************************************
*/
#include "AllItemExpr.h"
#include "AllRelExpr.h"
#include "RelPackedRows.h"
#include "SchemaDB.h"
#include "GroupAttr.h"
#include "BindWA.h"
#include "NormWA.h"
#include "Cost.h"
#include "CostMethod.h"
#include "opt.h"
#include "Globals.h"

// -----------------------------------------------------------------------
// This file contains all the methods for the class UnPackRows.
// This is a departure from the way other RelExpr's are organized.
//
  // UnPackRows constructor
  // Inputs:
  //
  //  maxPackingFactor: The packing factor of the packed table.  This value
  //  is stored so that the number of rows generated by this node
  //  can be estimated.
  //
  //  unPackExprs: This expression must contain a list of expressions to
  //  unpack the SYSKEY and all the user columns of the packed table.
  //  The SYSKEY is unpacked by 'calculating' the SYSKEY value based on
  //  an initial value of SYSKEY for the packed row and the current value
  //  of the index into the packed row.  The user columns are unpacked using
  //  the UnPackCol expression based on the value of the index.  The
  //  index is provided at runtime thru a hostVar.  The valueId of this
  //  HostVar must be captured so that it can be used to map a local
  //  variable into the workAtp of the work procedure.
  //
  //  packingFactor: This expression must contain a list of expressions to
  //  extract the NUMROWS field from each packed column.  During normalization,
  //  this list will be reduced to only one. (The value of NUMROWS is
  //  the same in each packed column).  This order of this list must
  //  correspond to the order of the unPackExprs list.
  //
  //  child: The child node of this node. presumably a Scan node.

  UnPackRows::UnPackRows(Lng32 maxPackingFactor,
			 ItemExpr  *unPackExprs,
			 ItemExpr  *packingFactor,
			 TableDesc *unPackedTable,
			 RelExpr   *child,
			 ValueId   indexHostVarValueId,
			 CollHeap *oHeap)
  : RelExpr(REL_UNPACKROWS, child, NULL, oHeap),
    maxPackingFactor_(maxPackingFactor),
    unPackExprTree_(unPackExprs),
    packingFactorTree_(packingFactor),
    unPackedTable_(unPackedTable),
    indexValue_(indexHostVarValueId),
    sysKeyId_(NULL_VALUE_ID),
    originalPreds_(NULL),
    rewrittenPreds_(NULL),
    rowwiseRowset_(FALSE),
    rwrsInputSizeExpr_(NULL),
    rwrsMaxInputRowlenExpr_(NULL),
    rwrsBufferAddrExpr_(NULL)
  {
    setNonCacheable();
  }

  UnPackRows::UnPackRows(Lng32 maxPackingFactor,
			 ItemExpr  *rwrsInputSizeExpr,
			 ItemExpr  *rwrsMaxInputRowlenExpr,
			 ItemExpr  *rwrsBufferAddrExpr,
			 RelExpr   *child,
			 CollHeap *oHeap)
  : RelExpr(REL_UNPACKROWS, child, NULL, oHeap),
    maxPackingFactor_(maxPackingFactor),
    unPackExprTree_(NULL),
    packingFactorTree_(NULL),
    unPackedTable_(NULL),
    indexValue_(NULL_VALUE_ID),
    sysKeyId_(NULL_VALUE_ID),
    originalPreds_(NULL),
    rewrittenPreds_(NULL),
    rowwiseRowset_(TRUE),
    rwrsInputSizeExpr_(rwrsInputSizeExpr),
    rwrsMaxInputRowlenExpr_(rwrsMaxInputRowlenExpr),
    rwrsBufferAddrExpr_(rwrsBufferAddrExpr)
  {
    setNonCacheable();
  }

// UnPackRows::~UnPackRows() -----------------------------------------------
// The destructor
//
UnPackRows::~UnPackRows()
{
}

// UnPackRows::topHash() --------------------------------------------------
// Compute a hash value for a chain of derived RelExpr nodes.
// Used by the Cascade engine as a quick way to determine if
// two nodes are identical.
// Can produce false positives (nodes appear to be identical),
// but should not produce false negatives (nodes are definitely different)
//
// Inputs: none (other than 'this')
//
// Outputs: A HashValue of this node and all nodes in the
// derivation chain below (towards the base class) this node.
//
HashValue UnPackRows::topHash()
{
  // Compute a hash value of the derivation chain below this node.
  //
  HashValue result = RelExpr::topHash();

  result ^= unPackExpr();
  result ^= packingFactor();

  return result;
}

// UnPackRows::duplicateMatch()
// A more thorough method to compare two RelExpr nodes.
// Used by the Cascades engine when the topHash() of two
// nodes returns the same hash values.
//
// Inputs: other - a reference to another node of the same type.
//
// Outputs: NABoolean - TRUE if this node is 'identical' to the
//          'other' node. FALSE otherwise.
//
// In order to match, this node must match all the way down the
// derivation chain to the RelExpr class.
//
// For the UnPackRows node, the only relevant data members which
// needs to be compared are unPackExpr_ and packingFactor_
//
NABoolean
UnPackRows::duplicateMatch(const RelExpr & other) const
{
  // Compare this node with 'other' down the derivation chain.
  //
  if (!RelExpr::duplicateMatch(other))
    return FALSE;

  // Cast the RelExpr to a UnPackRows node. (This must be a UnPackRows node)
  //
  UnPackRows &o = (UnPackRows &) other;

  // The tables described by both must be the same.
  //
  if(!(unPackedTable() == o.unPackedTable()))
    return FALSE;

  // If the unPackExprs are the same and the packingFactors are the same,
  // then the nodes are identical
  //
  if (!(unPackExpr() == o.unPackExpr()))
    return FALSE;

  if(!(packingFactor() == o.packingFactor()))
    return FALSE;

  if(!(rowwiseRowset() == o.rowwiseRowset()))
    return FALSE;
  
  return TRUE;
}

// UnPackRows::copyTopNode ----------------------------------------------
// Copy a chain of derived nodes (Calls RelExpr::copyTopNode).
// Needs to copy all relevant fields.
// Used by the Cascades engine.
//
// Inputs: derivedNode - If Non-NULL this should point to a node
//         which is derived from this node.  If NULL, then this
//         node is the top of the derivation chain and a node must
//         be constructed.
//
// Outputs: RelExpr * - A Copy of this node.
//
// If the 'derivedNode is non-NULL, then this method is being called
// from a copyTopNode method on a class derived from this one. If it
// is NULL, then this is the top of the derivation chain and an UnPackRows
// node must be constructed.
// 
// In either case, the relevant data members must be copied to 'derivedNode'
// and 'derivedNode' is passed to the copyTopNode method of the class
// below this one in the derivation chain (RelExpr::copyTopNode() in this
// case).
// 
RelExpr *
UnPackRows::copyTopNode(RelExpr *derivedNode, CollHeap *outHeap)
{
  UnPackRows *result;

  if (derivedNode == NULL)
    // This is the top of the derivation chain
    // Create an empty UnPackRows node.
    //
    {
      if (rowwiseRowset())
	result = new (outHeap) UnPackRows(0, 
					  (ItemExpr*)NULL, 
					  (ItemExpr*)NULL, 
					  (ItemExpr*)NULL, NULL);
      else
	result = new (outHeap) UnPackRows();
    }
  else
    // A node has already been constructed as a derived class.
    //
    result = (UnPackRows *) derivedNode;

  // Copy the relavant fields.

  result->unPackedTable() = unPackedTable();

  result->unPackExpr() = unPackExpr();

  result->packingFactor() = packingFactor();

  result->maxPackingFactor_ = getMaxPackingFactor();

  result->indexValue() = indexValue();

  result->sysKeyId() = sysKeyId();

  result->originalPreds() = originalPreds();
  result->rewrittenPreds() = rewrittenPreds();

  result->setRowwiseRowset(rowwiseRowset());

  // copy pointer to expressions
  // These are not available after bindNode()
  //
  if (unPackExprTree() != NULL)
    result->unPackExprTree() = unPackExprTree()->copyTree(outHeap)->castToItemExpr();

  if (packingFactorTree() != NULL)
    result->packingFactorTree() = 
      packingFactorTree()->copyTree(outHeap)->castToItemExpr();

  if (rwrsInputSizeExpr_ != NULL)
     result->rwrsInputSizeExpr_ = rwrsInputSizeExpr_->copyTree(outHeap)->castToItemExpr();

  if (rwrsMaxInputRowlenExpr_ != NULL)
     result->rwrsMaxInputRowlenExpr_ = rwrsMaxInputRowlenExpr_->copyTree(outHeap)->castToItemExpr();

  if (rwrsInputSizeExpr_ != NULL)
     result->rwrsBufferAddrExpr_ = rwrsBufferAddrExpr_->copyTree(outHeap)->castToItemExpr();

  result->rwrsOutputVids_ = rwrsOutputVids_;

  // Copy any data members from the classes lower in the derivation chain.
  //
  return RelExpr::copyTopNode(result, outHeap);
}


// UnPackRows::addLocalExpr() -----------------------------------------------
// Insert into a list of expressions all the expressions of this node and
// all nodes below this node in the derivation chain. Insert into a list of
// names, all the names of the expressions of this node and all nodes below
// this node in the derivation chain. This method is used by the GUI tool
// and by the Explain Function to have a common method to get all the
// expressions associated with a node.
//
// Inputs/Outputs: xlist - a list of expressions.
//                 llist - a list of names of expressions.
//
// The xlist contains a list of all the expressions associated with this
// node. The llist contains the names of these expressions. (This lists
// must be kept in the same order).
// UnPackRows::addLocalExpr potentially adds the unPackExpr_ expression
// ("unpack_expression") and the packingFactor_ expression ("packing_factor").
//
// It then calls RelExpr::addLocalExpr() which will add any RelExpr
// expressions to the list.
//
void UnPackRows::addLocalExpr(LIST(ExprNode *) &xlist,
                              LIST(NAString) &llist) const
{

  if (unPackExprTree() || unPackExpr().entries() > 0) {
    if(unPackExpr().isEmpty())
      xlist.insert(unPackExprTree_);
    else
      xlist.insert(unPackExpr().rebuildExprTree(ITM_ITEM_LIST));
    llist.insert("unpack_expression");
  }

  if (packingFactorTree() || packingFactor().entries() > 0) {
    if (packingFactor().isEmpty())
      xlist.insert(packingFactorTree_);
    else
      xlist.insert(packingFactor().rebuildExprTree(ITM_ITEM_LIST));
    llist.insert("packing_factor");
  }

  if(indexValue_ != NULL_VALUE_ID) {
    xlist.insert(indexValue_.getItemExpr());
    llist.insert("index_value");
  }

  RelExpr::addLocalExpr(xlist,llist);
}

// UnPackRows::getPotentialOutputValues() ---------------------------------
// Construct a Set of the potential outputs of this node.
//
// Inputs: none (other than 'this')
//
// Outputs: outputValues - a ValueIdSet representing the potential outputs
//          of this node.
//
// The potential outputs for the UnPackRows node are the new columns
// generated by the UnPackRows node. The new columns generated by UnPackRows
// are the syskey column and the value colunms.
//
void
UnPackRows::getPotentialOutputValues(ValueIdSet & outputValues) const
{
  // Make sure the ValueIdSet is empty.
  //
  outputValues.clear();

  // Add the values generated by the UnPackRows node.
  //
  outputValues.insertList(unPackExpr());

} // UnPackRows::getPotentialOutputValues()

void
UnPackRows::constructNewSyskeyPreds(ValueIdSet &predicates)
{
  if(sysKeyId() == NULL_VALUE_ID) {

    // Not Possible...
    //
    return;
  }

  ValueIdSet newPredicates;
  for(ValueId pred = predicates.init(); predicates.next(pred);
      predicates.advance(pred)) {

    // First see if this pred has already been rewritten...
    //
    CollIndex predIndex = 
      originalPreds() ? originalPreds()->index(pred) : NULL_COLL_INDEX;

    if (predIndex != NULL_COLL_INDEX) {
      newPredicates += rewrittenPreds()->at(predIndex);
    } else {

      // Try to match predicates of the form (SYSKEY OP VALUE)
      OperatorTypeEnum oper = pred.getItemExpr()->getOperatorType();

      if(oper == ITM_EQUAL ||
         oper == ITM_LESS ||
         oper == ITM_LESS_EQ ||
         oper == ITM_GREATER ||
         oper == ITM_GREATER_EQ) {
        
        if(pred.getItemExpr()->child(0) == sysKeyId()) {
          // A match

          ItemExpr *packedSyskey = 
            pred.getItemExpr()->child(0)->castToItemExpr()->child(0);

          ItemExpr *oldValue = pred.getItemExpr()->child(1);
          ItemExpr *maskConst = 
            new (CmpCommon::statementHeap()) SystemLiteral(65535);

          const NAType *desiredType = &packedSyskey->getValueId().getType();

          ItemExpr *maskValue =
            new (CmpCommon::statementHeap()) 
            Cast(maskConst, desiredType);

          oldValue =
            new (CmpCommon::statementHeap()) 
            Cast(oldValue, desiredType);
          
          ItemExpr *newValue = NULL;
          switch(oper) {
          case ITM_EQUAL:
            newValue = new (CmpCommon::statementHeap())
              Mask(ITM_MASK_CLEAR,oldValue, maskValue);
            break;
          case ITM_LESS:
            newValue = new (CmpCommon::statementHeap())
              Mask(ITM_MASK_SET, oldValue, maskValue);
            break;
          case ITM_LESS_EQ:
            newValue = new (CmpCommon::statementHeap())
              Mask(ITM_MASK_SET, oldValue, maskValue);
            oper = ITM_LESS;
            break;
          case ITM_GREATER:
            newValue = new (CmpCommon::statementHeap())
              Mask(ITM_MASK_CLEAR, oldValue, maskValue);
            oper = ITM_GREATER_EQ;
            break;
          case ITM_GREATER_EQ:
            newValue = new (CmpCommon::statementHeap())
              Mask(ITM_MASK_CLEAR, oldValue, maskValue);
            break;
          }

          ItemExpr *newPred = new (CmpCommon::statementHeap())
            BiRelat(oper, packedSyskey, newValue);
          
          newPred->synthTypeAndValueId();
        
          newPredicates += newPred->getValueId();

          if(!originalPreds()) {
            originalPreds() = new (CmpCommon::statementHeap()) ValueIdList();
            rewrittenPreds() = new (CmpCommon::statementHeap()) ValueIdList();
          }

          originalPreds()->insert(pred);
          rewrittenPreds()->insert(newPred->getValueId());

          CMPASSERT(originalPreds()->entries() == 
                    rewrittenPreds()->entries());
          
        }
      }
    }
  }

  predicates += newPredicates;
  
}

void 
UnPackRows::pushdownCoveredExpr(const ValueIdSet &outputExpr,
                                const ValueIdSet &newExternalInputs,
                                ValueIdSet &predicatesOnParent,
				const ValueIdSet *setOfValuesReqdByParent,
                                Lng32 childIndex
                                )
{

  ValueIdSet exprOnParent;
  if(setOfValuesReqdByParent)
    exprOnParent = *setOfValuesReqdByParent;
  exprOnParent += outputExpr ;
  ValueId refVal;
  ValueIdSet outputSet ;
  

  // Prune from the unPackExpr() ValueIdSet, those expressions
  // that are not needed above (in setOfValuesReqdByParent) or by
  // the selectionPred.
  //
  for(ValueId unPackCol = unPackExpr().init(); unPackExpr().next(unPackCol);
      unPackExpr().advance(unPackCol)) {
    if(!exprOnParent.referencesTheGivenValue(unPackCol, refVal) &&
       !selectionPred().referencesTheGivenValue(unPackCol, refVal)) {
      unPackExpr() -= unPackCol;
    }
  }

  // The packingFactor expr used to be modified here so that it
  // used one of the columns used by unPackExpr.  This code was
  // kind of kludgy, since it required knowledge of the packingFactor
  // expression.  Since this node is now being used by RowSets,
  // this code has been removed.

  // Remove all expressions from exprOnParent.  They
  // can't be pushed down!
  //
  exprOnParent.clear();

  // Add all the values required for the UnPackRows expressions
  // to the values required by the parent.  These expression
  // can't be pushed down either, but attempting to push them
  // down causes the child node to provide the values needed.
  //
  outputSet += unPackExpr();

  exprOnParent += packingFactor();


  constructNewSyskeyPreds(predicatesOnParent);
  
  ValueIdSet emptySet;
  RelExpr::pushdownCoveredExpr(outputSet,
                               newExternalInputs,
                               predicatesOnParent,
			       &emptySet,
                               childIndex);

} // UnPackRows::pushdownCoveredExpr

// UnPackRows::removeUnPackExprTree() -------------------------------------
// Return the unPackExprTree_ ItemExpr tree and set to NULL,
//
// Inputs: none (Other than 'this')
//
// Outputs: ItemExpr * - the value of unPackExprTree_
//
// Side Effects: Sets the value of unPackExprTree_ to NULL.
//
// Called by UnPackRows::bindNode(). The value of unPackExprTree_ is not
// needed after the binder.
//
ItemExpr *
UnPackRows::removeUnPackExprTree()
{
  ItemExpr *result = unPackExprTree();
  unPackExprTree() = (ItemExpr *)NULL;
  return result;
}

// UnPackRows::removePackingFactorTree() -------------------------------------
// Return the packingFactorTree_ ItemExpr tree and set it to NULL,
//
// Inputs: none (Other than 'this')
//
// Outputs: ItemExpr * - the value of packingFactorTree_
//
// Side Effects: Sets the value of packingFactorTree_ to NULL.
//
// Call by UnPackRows::bindNode(). The value of keyCol_ is not
// needed after the binder.
//
ItemExpr *
UnPackRows::removePackingFactorTree()
{
  ItemExpr *result = packingFactorTree();
  packingFactorTree() = (ItemExpr *)NULL;
  return result;
}

// The destructor
//
PhysUnPackRows::~PhysUnPackRows()
{
}

// PhysUnPackRows::copyTopNode ----------------------------------------------
// Copy a chain of derived nodes (Calls UnPackRows::copyTopNode).
// Needs to copy all relevant fields.
// Used by the Cascades engine.
//
// Inputs: derivedNode - If Non-NULL this should point to a node
//         which is derived from this node.  If NULL, then this
//         node is the top of the derivation chain and a node must
//         be constructed.
//
// Outputs: RelExpr * - A Copy of this node.
//
// If the 'derivedNode is non-NULL, then this method is being called
// from a copyTopNode method on a class derived from this one. If it
// is NULL, then this is the top of the derivation chain and a UnPackRows
// node must be constructed.
// 
// In either case, the relevant data members must be copied to 'derivedNode'
// and 'derivedNode' is passed to the copyTopNode method of the class
// below this one in the derivation chain (UnPackRows::copyTopNode() in this
// case).
// 
RelExpr *
PhysUnPackRows::copyTopNode(RelExpr *derivedNode, CollHeap *outHeap)
{
  PhysUnPackRows *result;

  if (derivedNode == NULL)
    // This is the top of the derivation chain
    // Generate an empty PhysUnPackRows node.
    //
    result = new (outHeap) PhysUnPackRows();
  else
    // A node has already been constructed as a derived class.
    //
    result = (PhysUnPackRows *) derivedNode;

  // PhysUnPackRows has no data members.

  // Copy any data members from the classes lower in the derivation chain.
  //
  return UnPackRows::copyTopNode(result, outHeap);
}

// UnPackRows::transformNode() -------------------------------------------
// Unconditional query transformations such as the transformation of
// a subquery to a semijoin are implemented by the virtual function
// transformNode(). The aim of such transformations is to bring the
// query tree to a canonical form. transformNode() also ensures
// that the "required" (or characteristic) input values are "minimal"
// and the "required" (or characteristic) outputs values are
// "maximal" for each operator.
//
// transformNode() is an overloaded name, which is used for a set
// of methods that implement the transformation phase of query
// normalization.
//
// We use the term query tree for a tree of relational operators,
// each of which can contain none or more scalar expression trees.
// The transformations performed by transformNode() brings scalar
// expressions into a canonical form. The effect of most such
// transformations is local to the scalar expression tree.
// However, the transformation of a subquery requires a semijoin
// to be performed between the relational operator that contains
// the subquery and the query tree for the subquery. The effect
// of such a subquery transformation is therefore visible not
// only in the scalar expression tree but also in the relational
// expression tree.
//
// Parameters:
//
// NormWA & normWARef
//    IN : a pointer to the normalizer work area
//
// ExprGroupId & locationOfPointerToMe
//    IN : a reference to the location that contains a pointer to
//         the RelExpr that is currently being processed.
//
void UnPackRows::transformNode(NormWA &normWARef,
                              ExprGroupId &locationOfPointerToMe)
{
  CMPASSERT( this == locationOfPointerToMe );

  // If this node has already been transformed, we are done.
  //
  if (nodeIsTransformed())
    return;
  
  // Make sure that it is only transformed once.
  //
  markAsTransformed();

  // transformNode takes up a bound tree and turns into a transformed
  // tree. For a RelExpr that means the following.
  //    + expressions are transformed. If the expressions contain
  //        subqueries then new RelExpr are created for them and
  //        they are usually added above (as an ancestor) of the node
  //        that contained them.
  //    + predicates are pulled up from the children and their
  //        required inputs are modified
  //    + the required inputs of the node the node itself are changed
  //        from being a sufficient set to being a sufficient minimal
  //        set.
  //
  // Transform the child.
  // Pull up their transformed predicates
  // recompute their required inputs.
  //
  child(0)->transformNode(normWARef, child(0)); 

  /*

  // Prune the unPackExpr early on for performance reasons in trasnformation
  // and normalization so that it contains columns which are referenced in
  // the query.
  //

  // Have at least two unpackcol item there to handle cases like count(*).
  //
  CMPASSERT(unPackExpr().entries() >= 2);

  for(ValueId unPackCol = unPackExpr().init(); unPackExpr().next(unPackCol);
      unPackExpr().advance(unPackCol)) {
    BaseColumn *bcol =
      (BaseColumn *)(unPackCol.getItemExpr()->child(0)->castToItemExpr());

    if(!bcol->getNAColumn()->isReferenced()) {
      unPackExpr() -= unPackCol;
    }
  }


  ValueIdList newUnPackExpr;
  ValueIdList newPackingFactor;
  newUnPackExpr.insert(unPackExpr()[0]);
  newPackingFactor.insert(packingFactor()[0]);

  for(CollIndex i = 1; i < unPackExpr().entries(); i++)
  {
    if (unPackExpr()[i].getItemExpr()->getOperatorType() 
        == ITM_ROWSETARRAY_SCAN) {
      newUnPackExpr.insert(unPackExpr()[i]);   
    }
  }

  unPackExpr().clear();
  unPackExpr().insert(newUnPackExpr);
  packingFactor().clear();
  packingFactor().insert(newPackingFactor);
  */

  if(unPackExpr().transformNode(normWARef,
                                child(0),
                                getGroupAttr()->getCharacteristicInputs())) {
    // -----------------------------------------------------------------
    // Transform my new child.
    // -----------------------------------------------------------------
    child(0)->transformNode(normWARef, child(0));
  }

  if(packingFactor().
     transformNode(normWARef,
                   child(0),
                   getGroupAttr()->getCharacteristicInputs())) {
    // -----------------------------------------------------------------
    // Transform my new child.
    // -----------------------------------------------------------------
    child(0)->transformNode(normWARef, child(0));
  }

  // Since unPackExpr() and packingFactor() refer to indexValue,
  // indexValue() may have been replaced with something else...
  //
  if (indexValue() != NULL_VALUE_ID)
    indexValue() = 
      indexValue().getItemExpr()->getReplacementExpr()->getValueId();
  
  // Pull up the predicates and recompute the required inputs
  // of whoever my children are now.
  //
  pullUpPreds();

  // transform the selection predicates
  //
  transformSelectPred(normWARef, locationOfPointerToMe);

} // UnPackRows::transformNode()

// UnPackRows::rewriteNode() ---------------------------------------------
// rewriteNode() is the virtual function that computes
// the transitive closure for "=" predicates and rewrites value
// expressions.
//
// Parameters:
//
// NormWA & normWARef
//    IN : a pointer to the normalizer work area
//
void UnPackRows::rewriteNode(NormWA & normWARef)
{
  RelExpr::rewriteNode(normWARef);

  if(unPackExpr().normalizeNode(normWARef)) {
  }

  if(packingFactor().normalizeNode(normWARef)) {
  }

  // Since unPackExpr() and packingFactor() refer to indexValue,
  // indexValue() may have been replaced with something else...
  //
  if (indexValue() != NULL_VALUE_ID)
    indexValue() = 
      indexValue().getItemExpr()->getReplacementExpr()->getValueId();

} // UnPackRows::rewriteNode()

// UnPackRows::pullUpPreds() --------------------------------------------
// is redefined to disallow the pullup of predicates
// from the operator's child.  UnPackRows can not pull up
// any predicates from its child since no of the outputs of the
// child are outputs of the UnPackRows.
//
void UnPackRows::pullUpPreds()
{
  // ---------------------------------------------------------------------
  // Simply don't pull up child's selection predicates. Still need to tell
  // child to recompute its outer references due to the warning below.
  // ---------------------------------------------------------------------
  child(0)->recomputeOuterReferences();

  // ---------------------------------------------------------------------
  // WARNING: One rule that this procedure must follow is
  // that recomputeOuterReferences() must be called on the children even
  // if no predicates are pulled up from them. This is to correct
  // the outer references that are added to a right child of a
  // semi or outer join when processing subqueries in the ON clause.
  // ---------------------------------------------------------------------
}

// UnPackRows::recomputeOuterReferences() --------------------------------
// This method is used by the normalizer for recomputing the
// outer references (external dataflow input values) that are
// still referenced by each operator in the subquery tree
// after the predicate pull up is complete.
//
// Side Effects: sets the characteristicInputs of the groupAttr.
//
void UnPackRows::recomputeOuterReferences()
{
  // This is virtual method on RelExpr.
  // When this is called it is assumed that the children have already
  // been transformed.
  // The required inputs of the child are therefore already minimal
  // and sufficient.
  // It is also assumed that the RelExpr itself has been bound.
  // That implies that the group attributes have already been allocated
  // and the required inputs is a sufficient (but not necessarilly minimum)
  // set of external values needed to evaluate all expressions in this subtree.
  // 
  // Delete all those input values that are no longer referenced on
  // this operator because the predicates that reference them have 
  // been pulled up.
  //
  ValueIdSet outerRefs = getGroupAttr()->getCharacteristicInputs(); 

  // The set of valueIds need by this node.
  //
  ValueIdSet allMyExpr(getSelectionPred());

  allMyExpr += unPackExpr();
  allMyExpr += packingFactor();
  
  // Remove from outerRefs those valueIds that are not needed
  // by all my expressions
  //
  allMyExpr.weedOutUnreferenced(outerRefs);

  // Add to outerRefs those that my children need.
  //
  outerRefs += child(0).getPtr()->getGroupAttr()->getCharacteristicInputs();

  // set my Character Inputs to this new minimal set.
  //
  getGroupAttr()->setCharacteristicInputs(outerRefs);
} // UnPackRows::recomputeOuterReferences()  


// UnPackRows::synthEstLogProp() ------------------------------------------
// synthesize estimated logical properties given a specific set of
// input log. properties.
//
// Parameters:
//
// EstLogPropSharedPtr inputEstLogProp
//    IN : A set of input logical properties used to estimate the logical
//         properities of this node.
//
void UnPackRows::synthEstLogProp(const EstLogPropSharedPtr& inputEstLogProp)
{
  if (getGroupAttr()->isPropSynthesized(inputEstLogProp) == TRUE) 
    return;

  // Create a new Output Log Property
  //
  // EstLogPropSharedPtr myEstProps (
  //    new(CmpCommon::statementHeap()) EstLogProp());

  // Get the current column stats.
  //
  // const ColStatDescList &outerColStatsList = inputEstLogProp->getColStats();

  // NEED to compute new colstats for this node.
  // !!!

  // Get the estimated logical properties of the child. To be used
  // to estimate the logical properties of this node.
  //
     EstLogPropSharedPtr childEstProps = child(0).outputLogProp(inputEstLogProp);

     CostScalar rowCount = 
        childEstProps->getResultCardinality() * getMaxPackingFactor();
  
  // Set the cardinality estimate.
  //
  // myEstProps->setResultCardinality(rowCount);

  // UnPackRows node behaves like a scan of the unpacked logical table in
  // the sense that it is responsible for picking up histogram statistics 
  // for the logical rows.
  //

     EstLogPropSharedPtr myEstProps;

     if (unPackedTable())
       myEstProps = synthEstLogPropForUnaryLeafOp (inputEstLogProp,
                                      colStats(),
                                      //unPackedTable()->getTableColStats(),
                                      Cardinality(1.));
     else
     {
	myEstProps = EstLogPropSharedPtr(new (STMTHEAP) EstLogProp (rowCount));
	
	// For each characteristics output of this group, we want to create a 
	// histogram

	ValueIdSet reqdOutputs = getGroupAttr()->getCharacteristicOutputs();
	ColStatDescList & outputColStats =  myEstProps->colStats();

	for (ValueId charOutput = reqdOutputs.init();
	  reqdOutputs.next(charOutput);
	  reqdOutputs.advance(charOutput) )
	  {
		outputColStats.addColStatDescForVirtualCol(rowCount,
						  rowCount,
						  charOutput,
						  charOutput,
						  charOutput,
                                                  NULL);
	  }
     }
      

  // Set the logical properties of this node.
  //
  getGroupAttr()->addInputOutputLogProp(inputEstLogProp, myEstProps);
  
} // UnPackRows::synthEstLogProp


// UnPackRows::synthLogProp ----------------------------------------------
// synthesize logical properties
//
void
UnPackRows::synthLogProp(NormWA * normWAPtr)
{
  // check to see whether properties are already synthesized.
  if (getGroupAttr()->existsLogExprForSynthesis()) 
    return;

  // Need to check for cardinality constraints and uniques constraints.
  // !!!

  RelExpr::synthLogProp(normWAPtr);

  if (unPackedTable() == NULL) 
    {
      // Nothing else to do here. This is a rowset since we do not have
      // a table.
      return;
    }

  // The columns whose statistics are to be fetched are marked referenced
  // in the packed table's NATable. We have to transfer this information
  // to the unpacked table's NATable.
  //

  // unpacked table's NATable object.
  const NATable *unPackedNATable = unPackedTable()->getNATable();

  // Get the original table name.
  QualifiedName tableName = unPackedNATable->getTableName();

  // Prepend original name with "PACKED__@" to give the packed table name.
  NAString packedTableName( PACKED__, CmpCommon::statementHeap());
  packedTableName += tableName.getObjectName();
  tableName.setObjectName(packedTableName);

  // Need to construct the ExtendedQualName to lookup NATableDB.
  ExtendedQualName extendedTableName (tableName);
  extendedTableName.setSpecialType(ExtendedQualName::VIRTUAL_TABLE);
  
  // Fetch the packed table's NATable object.
  NATable *packedNATable = ActiveSchemaDB()->getNATableDB()->get(&extendedTableName);
  CMPASSERT(packedNATable);

  const NAColumnArray packedColArray = packedNATable->getNAColumnArray();
  const NAColumnArray unPackedColArray = unPackedNATable->getNAColumnArray();

  // The columns in packed table are ordered the same way as in unpacked table.
  CollIndex i = 0;
  for(i = 0; i < packedColArray.entries(); i++)
  {
    // Mark corresponding columns in the unpacked table referenced for columns
    // referenced in the packed table.
    //
    if(packedColArray[i]->isReferencedForMultiIntHist())
    {
      unPackedColArray[i]->setReferencedForMultiIntHist();
    } 
    else if(packedColArray[i]->isReferencedForSingleIntHist())
    {
      unPackedColArray[i]->setReferencedForSingleIntHist();
    } 
    else if(packedColArray[i]->isReferenced())
    {
      unPackedColArray[i]->setReferenced();
    }
  }

  // Fetch the colstat for the logical table columns which are referenced.
  // Map them to there corresponding UnPackCol expressions which are used
  // above me.
  // 

  // This triggers the fetch of histogram statistics which are then stored
  // in the logical unpacked table descriptor as well as returned.
  //
  ColStatDescList &initialStats = unPackedTable()->tableColStats();

  // Find out the base columns of all entries in unPackExpr_ and stored them
  // in an array for later use.
  //
  LIST(BaseColumn *) packedBaseColList(STMTHEAP);
  for(ValueId unPackCol = unPackExpr().init(); unPackExpr().next(unPackCol);
      unPackExpr().advance(unPackCol))
    {
      ItemExpr *packedItem = unPackCol.getItemExpr()->child(0);
      BaseColumn *baseCol;

      // Probably the case here since everything has been converted to VEGref.
      //
      if(packedItem->getOperatorType() != ITM_BASECOLUMN)
        {
          if(packedItem->getOperatorType() == ITM_VEG_REFERENCE)
            {
              VEGReference *packedVEGRef = (VEGReference *) packedItem;
              ValueIdSet packedVEGset = packedVEGRef->getVEG()->getAllValues();
              CMPASSERT(packedVEGset.entries() > 0);

              // Locate the base column entry in the VEG.
              //
              NABoolean found = FALSE;
              for(ValueId colvid = packedVEGset.init();
                  packedVEGset.next(colvid);
                  packedVEGset.advance(colvid))
                {
                  if(colvid.getItemExpr()->getOperatorType() == ITM_BASECOLUMN)
                    {
                      baseCol = (BaseColumn *)colvid.getItemExpr();
                      packedBaseColList.insert(baseCol);
                      found = TRUE;
                      break;
                    }
                }
              if(!found) packedBaseColList.insert(NULL);
            }
          else
            // Left child neither a base column nor a VEGref. Problems...
            packedBaseColList.insert(NULL);
        }
      else packedBaseColList.insert((BaseColumn *)packedItem);
    }

  // To-remove list to keep track of statistics to be removed from the Col
  // StatDescList, which are not actually referenced.
  //
  ColStatDescList removeList(CmpCommon::statementHeap());

  // We need to associate statistics of each logical column with its corr.
  // UnPackCol expression, since it is in this form that the logical column
  // is known above this UnPackRows node.
  //
  for (i = 0; i < initialStats.entries(); i++)
  {
    // Column this statistics is associated with.
    //
    const NAColumnArray &statCols =
      initialStats[i]->getColStats()->getStatColumns();
    
    // Single column histogram only right now.
    //
    if (statCols.entries() != 1) continue;
    
    // Find out which logical column it is.
    //
    NABoolean found = FALSE;
    CollIndex j = 0;
    for(ValueId unPackCol = unPackExpr().init();
        unPackExpr().next(unPackCol);
        unPackExpr().advance(unPackCol), j++)
      {
        if (packedBaseColList[j] &&
            packedBaseColList[j]->getColName() == statCols[0]->getColName())
          {
            // Got it! Associate corresponding unpackcol with that ColStatDesc.
            //
            found = TRUE;
            initialStats[i]->VEGColumn() = unPackCol;
            break;
          }
      }
    // Erase entry from my ColStatDesc, since it's not used here.
    if (!found) removeList.insert(initialStats[i]);
    else colStats().insert(initialStats[i]);
  }

  // Remove unused statistics.
  //
  for (i = 0; i < removeList.entries(); i++) 
    initialStats.remove(removeList[i]);

} // UnPackRows::synthLogProp()


// UnPackRows::costMethod()
// Obtain a pointer to a CostMethod object providing access
// to the cost estimation functions for nodes of this type.
CostMethod*
PhysUnPackRows::costMethod() const
{
  static THREAD_P CostMethodUnPackRows *m = NULL;
  if (m == NULL)
    m = new (GetCliGlobals()->exCollHeap()) CostMethodUnPackRows();
  return m;

} // PhysUnPackRows::costMethod()

//<pb>
//==============================================================================
//  Synthesize physical properties for Unpack operator's current plan 
// extracted from a spcified context.
//
// Input:
//  myContext  -- specified context containing this operator's current plan.
//
//  planNumber -- plan's number within the plan workspace.  Used optionally for
//                 synthesizing partitioning functions but unused in this
//                 derived version of synthPhysicalProperty().
//
// Output:
//  none
//
// Return:
//  Pointer to this operator's synthesized physical properties.
//
//==============================================================================
PhysicalProperty *
PhysUnPackRows::synthPhysicalProperty(const Context *context,
                                      const Lng32    planNumber,
                                      PlanWorkSpace  *pws)
{
  const PhysicalProperty* const sppOfTheChild =
                 context->getPhysicalPropertyOfSolutionForChild(0);

  // for now, simply propagate the physical property
  PhysicalProperty *unPackPP = 
	new(CmpCommon::statementHeap()) PhysicalProperty(*sppOfTheChild);

  if (unPackedTable() == NULL) { // we're a rowset unpack
    if (getGroupAttr()->getResultCardinalityForEmptyInput() > 
        ActiveSchemaDB()->getDefaults().getAsLong(COMP_INT_5)) {
      // rowset unpack should run in master because it's inefficient and
      // risky to send rowsets from master to an ESP. An ESP eventually
      // flows the rowset's data back to the master. An ESP parallel plan
      // fragment with a large rowset can exceed IPC size limit and crash
      // at run-time (see genesis case 10-060510-3471).
      unPackPP->setLocation(EXECUTE_IN_MASTER);
    }
  }
  return unPackPP;
} //  UnPackRows::synthPhysicalProperty() 

// UnPackRows::bindNode - Bind the UnPackRows node.
// This node has been generated by Scan::bindNode after it determined
// that this table is packed.  This node has two item expressions:
// 
// unPackExprTree(): This expression contains a list
// of expressions to unpack the SYSKEY and all the user columns
// of the packed table.  The SYSKEY is unpacked by 'calculating'
// the SYSKEY value based on an initial value of SYSKEY for the
// packed row and the current value of the index into the packed
// row.  The user columns are unpacked using the UnPackCol expression
// based on the value of the index.  The index is provided at runtime
// thru a hostVar.  The valueId of this HostVar must Refbe captured so
// that it can be used to map a local variable into the workAtp of
// the work procedure.
//
// packingFactorTree().  This expression contains a list
// of expressions to extract the NUMROWS field from each packed
// column.  During normalization, this list will be reduced to
// only one. (The value of NUMROWS is the same in each packed column).
//
//
RelExpr *UnPackRows::bindNode(BindWA *bindWA)
{
  
  // If this node has already been bound, we are done.
  //
  if (nodeIsBound())
    return this;

  // Bind the child nodes.
  //
  // Set the bindWA's view name resolution pointer to NULL before binding,
  // since no objects below this level either (1) need to be recorded as
  // dependencies for a view or (2) need to be linked back to their names
  // in the original view text.
  //
  ParNameLocList *saveNameLocList = bindWA->getNameLocListPtr();
  bindWA->setNameLocListPtr(NULL);  

  bindChildren(bindWA);
  if (bindWA->errStatus())
    return this;

  // Bind the UnPackExprTree.  This expression contains a list
  // of expressions to unpack the SYSKEY and all the user columns
  // of the packed table.  The SYSKEY is unpacked by 'calculating'
  // the SYSKEY value based on an initial value of SYSKEY for the
  // packed row and the current value of the index into the packed
  // row.  The user columns are unpacked using the UnPackCol expression
  // based on the value of the index.  The index is provided at runtime
  // thru a hostVar.
  //
  ValueIdList unPackExprList;
  if(unPackExprTree()) {
   
   // For rowsets, if the type is external (for instance, decimal or varchar), 
   // we must first convert to our internal equivalent type
   if (unPackExprTree()->origOpType() == ITM_ROWSETARRAY_SCAN) {
      const NAType *elemType =  ((RowsetArrayScan *) unPackExprTree())->getType();
      if (elemType->isExternalType()) {
        NAType *internalType = elemType->equivalentType();
	unPackExprTree() = new (bindWA->wHeap()) Cast(unPackExprTree(), internalType);
      }
    }

    if (unPackExprTree()->origOpType() == ITM_ITEM_LIST) {
      ItemExpr *exp = unPackExprTree();
      while (exp) {
        ItemExpr *elem;
	for (Int32 i = 0; i < exp->getArity(); i++) {
	  elem = (ItemExpr *) (exp->getChild(i));
	  if (elem->origOpType() == ITM_ROWSETARRAY_SCAN) {
	    const NAType *elemType =  ((RowsetArrayScan *) elem)->getType();
	    if (elemType->isExternalType()) {
	      NAType *internalType = elemType->equivalentType();
	      exp->child(i) = new (bindWA->wHeap()) Cast(elem, internalType);
	    }
	  }
	}

	if ( ((ItemExpr *) (exp->getChild(0)))->origOpType() != ITM_ITEM_LIST) {
	  break;
	}

	exp = (ItemExpr *) (exp->getChild(0));
      }
    }


    // $$$ Trick bindwa to believe this expression to bind is just one in
    // $$$ RI constraint so that it won't mark columns referenced. Better
    // $$$ invent something new for real.
    // 
    bindWA->getCurrentScope()->context()->inRIConstraint() = TRUE;

    removeUnPackExprTree()->convertToValueIdList(unPackExprList,
                                                 bindWA,
                                                 ITM_ITEM_LIST);

    unPackExpr().insertList(unPackExprList);

    bindWA->getCurrentScope()->context()->inRIConstraint() = FALSE;

    if (bindWA->errStatus())
      return this;
  }

  // Bind the packingFactorTree.  This expression contains an
  // expression to extract the NUMROWS field from the first packed
  // column of the smallest Access Path. 
  //
  if(packingFactorTree()) {

    // $$$ Trick bindwa to believe this expression to bind is just one in
    // $$$ RI constraint so that it won't mark columns referenced. Better
    // $$$ invent something new for real.
    // 
    bindWA->getCurrentScope()->context()->inRIConstraint() = TRUE;

    removePackingFactorTree()->convertToValueIdSet(packingFactor(),
                                                   bindWA,
                                                   ITM_ITEM_LIST);

    bindWA->getCurrentScope()->context()->inRIConstraint() = FALSE;

    if (bindWA->errStatus())
      return this;
  }
  
  // Search for the valueId of the HostVar used for the index of the
  // packed row.  We must keep track of this so that we can map
  // a variable local to the work procedure to be this valueid.
  // 
  // We should have allocated a ValueId for the host variable at this
  // point, but just in case

  if (unPackExprTree()) {
    if(indexValue_ == NULL_VALUE_ID) {
      ValueIdSet leafValues;
      
      unPackExprList[0].getItemExpr()->getLeafValueIds(leafValues);
      
      for(ValueId leaf = leafValues.init();
	  leafValues.next(leaf);
	  leafValues.advance(leaf)) {
	if(leaf.getItemExpr()->getOperatorType() == ITM_HOSTVAR) {
	  indexValue_ = leaf;
	}
      }
    }
    
    CMPASSERT(indexValue_ != NULL_VALUE_ID);
  }

  if (rwrsInputSizeExpr())
    {
      rwrsInputSizeExpr_ = rwrsInputSizeExpr_->bindNode(bindWA);
      if (bindWA->errStatus ())
	return NULL;
    }

  if (rwrsMaxInputRowlenExpr())
    {
      rwrsMaxInputRowlenExpr_ = rwrsMaxInputRowlenExpr_->bindNode(bindWA);
      if (bindWA->errStatus ())
	return NULL;
    }

  if (rwrsBufferAddrExpr())
    {
      rwrsBufferAddrExpr_ = rwrsBufferAddrExpr_->bindNode(bindWA);
      if (bindWA->errStatus ())
	return NULL;
    }

  // Construct the RETDesc for this node.
  //
  RETDesc *resultTable = new(bindWA->wHeap()) RETDesc(bindWA);

  // Add the columns from the child to the RETDesc.
  // 
  const RETDesc &childTable = *child(0)->getRETDesc();

  const ColumnDescList *sysColList = childTable.getSystemColumnList();

  // This ASSUMES that the columns in the child's RETDesc are
  // in the same order as the expressions in unPackExpr().
  // This is basically assuming that the child (Scan) produces
  // the RETDesc in the same order as they appear in the NATable.
  // This should probably be fixed by passing a list of ColRefNameObj()
  // to the UnPackRows node.
  //
  CollIndex j = 0;
  CollIndex i = 0;
  for(i = 0; i < sysColList->entries(); i++) {

    resultTable->addColumn(bindWA,
                           sysColList->at(i)->getColRefNameObj(),
			   (rowwiseRowset() ? sysColList->at(i)->getValueId()
			    : unPackExprList[j++]),
                           SYSTEM_COLUMN,
                           sysColList->at(i)->getHeading());

    if(sysColList->entries() == 1) {
      sysKeyId_ = unPackExprList[j-1];
    } else {
      sysKeyId_ = NULL_VALUE_ID;
    }
  }

  // if this is rowwise rowset, then child's output values, which are
  // the extracted values from the rowwise rowset buffer, are sent
  // to my parent. 
  // The value ids of the params from my child are used in expressions
  // above this node.
  // Get all the values of my children and remember them.

  for(i = 0; i < childTable.getColumnList()->entries(); i++) {
    
    if (rowwiseRowset())
      rwrsOutputVids_.insert(childTable.getValueId(i));

    resultTable->addColumn(bindWA,
                           childTable.getColRefNameObj(i),
			   (rowwiseRowset() ? childTable.getValueId(i)
			    : unPackExprList[j++]),
                           USER_COLUMN,
                           childTable.getHeading(i));
  }

  // If the Table Desc for the logical unpacked table is set, map the vid's
  // in there to the real vid's generated by unPackExpr_, assuming they are
  // ordered the same way.
  //
  if(unPackedTable())
  {
    unPackedTable()->clearColumnList();
    unPackedTable()->addToColumnList(unPackExprList);
  }

  // Set the return descriptor
  //
  setRETDesc(resultTable);
  bindWA->getCurrentScope()->setRETDesc(resultTable);

  //
  // Bind the base class.
  //
  // Restore the name resolution pointer
  //
  bindWA->setNameLocListPtr(saveNameLocList);
  return bindSelf(bindWA);
} // UnPackRows::bindNode()

